"""
活动页面组件。
"""
import abc
import typing
from loguru import logger
from .base import BaseSession, BaseSessionType, BaseSessionParentType
from .framework import dispatch, session_received
from .layer import Layer
from .utils import callback, to_any, Int32Value, Int64Value


@session_received
class Activity(BaseSession):
    """
    一个Activity会话表示一个活动任务，与安卓端的Activity类似，拥有相同的生命周期。
    在一个Activity钟，可以有多个Layer层，用于显示内容。
    """
    name = 'Activity'

    def __init__(self, context, *args, **kwargs):
        super().__init__(context, *args, **kwargs)
        self._layers = []
        self._paused = True

    def peek_top_layer(self) -> typing.Optional[Layer]:
        """
        查看栈顶的layer对象。
        """
        if not self._layers:
            return None
        return self._layers[-1]

    @classmethod
    async def create(
            cls: typing.Type[BaseSessionType], parent: BaseSessionParentType, *args: typing.Any, **kwargs: typing.Dict
    ) -> BaseSessionType:
        return await super().create(parent, *args, **kwargs)

    async def push_layer(self, layer: Layer):
        """
        把某个layer压入栈顶。
        @layer: 要压入栈顶的Layer实现。
        """
        top_layer = self.peek_top_layer()
        if top_layer:
            await top_layer.on_hide()
        self._layers.append(layer)
        await layer.on_show()

    async def pop_layer(self) -> typing.Optional[Layer]:
        """
        把栈顶的layer取出，返回已经取出的实例对象。
        """
        try:
            layer = self._layers.pop()
            await layer.on_hide()
        except IndexError:
            layer = None
        top_layer = self.peek_top_layer()
        if top_layer:
            await top_layer.on_show()
        return layer

    @property
    def layers(self) -> typing.List[Layer]:
        """
        获取所有的layer层。
        """
        return self._layers

    @abc.abstractmethod
    async def on_start(self):
        """
        当设备端的活动任务从后台恢复时自动调用此方法，可重写此方法来实现自己的业务代码。
        """
        if self.is_debug:
            logger.debug('%s: onStart.' % self.__class__.__name__)

    @abc.abstractmethod
    async def on_stop(self):
        """
        当设备端的活动任务从前台切换到后台时自动调用此方法，可重写此方法来实现自己的业务代码。
        """
        if self.is_debug:
            logger.debug('%s: onStop.' % self.__class__.__name__)

    @abc.abstractmethod
    async def on_resume(self):
        """
        当设备端的活动任务从暂停模式恢复时自动调用此方法，可重写此方法来实现自己的业务代码。
        """
        if self.is_debug:
            logger.debug('%s: onResume.' % self.__class__.__name__)
        self._paused = False
        top_layer = self.peek_top_layer()
        if top_layer:
            await top_layer.on_resume()

    @abc.abstractmethod
    async def on_pause(self):
        """
        当设备端的活动任务进入暂停模式时自动调用此方法，可重写此方法来实现自己的业务代码。
        """
        if self.is_debug:
            logger.debug('%s: onPause.' % self.__class__.__name__)
        self._paused = True
        top_layer = self.peek_top_layer()
        if top_layer:
            await top_layer.on_pause()

    async def on_back_pressed(self) -> bool:
        """
        当用户按下设备的返回键后触发。
        返回True表示不拦截事件，返回False表示拦截事件。
        如果当前layer站不为空，则交给栈顶layer处理。
        """
        if self.is_debug:
            logger.debug('%s: onBackPressed.' % self.__class__.__name__)
        top_layer = self.peek_top_layer()
        if top_layer:
            return await top_layer.on_back_pressed()
        return True

    async def _on_back_pressed(self, cont):
        """
        接收到事件后要给设备端返回结果，这时候必须新建一个携程来处理。
        """
        self.send_callback_result(cont, await self.on_back_pressed())

    async def on_receive(self, msg):
        """
        转发设备端的事件动作。
        """
        coro = self.on_start() if msg.name == 'onStart' else \
            self.on_stop() if msg.name == 'onStop' else \
            self.on_resume() if msg.name == 'onResume' else \
            self.on_pause() if msg.name == 'onPause' else \
            self._on_back_pressed(*msg.argv) if msg.name == 'onBackPressed' else \
            self.on_get_screen_size(
                *msg.argv, sessionId=msg.session, futureId=msg.future
            ) if msg.name == 'onGetScreenSize' else \
            self.on_get_title(
                *msg.argv, session_id=msg.session, future_id=msg.future
            ) if msg.name == 'onGetTitle' else \
            self.on_get_clipboard_text(
                *msg.argv, session_id=msg.session, future_id=msg.future
            ) if msg.name == 'onGetClipboardText' else \
            self.on_is_scrollable(
                *msg.argv, session_id=msg.session, future_id=msg.future
            ) if msg.name == 'onIsScrollable' else None
        if coro:
            dispatch(coro)
        else:
            await super().on_receive(msg)

    @callback(str)
    async def on_get_title(self, title, session_id=0, future_id=0):
        """
        当获取标题成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, title)

    async def get_title(self) -> str:
        """
        获取标题。
        调用示例：
        print(await activity.get_title())
        """
        return await self.send_action_until_return('getTitle')

    async def set_title(self, title: str):
        """
        设置标题。
        @title: 要显示的标题。
        调用示例：
        await activity.set_title('欢迎来到逸时光!')
        """
        dispatch(self.send_action('setTitle', to_any(title)))

    @callback(bool)
    async def on_is_scrollable(self, is_scrollable, session_id=0, future_id=0):
        """
        当获取可滚动状态成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, is_scrollable)

    async def is_scrollable(self) -> bool:
        """
        获取可滚动状态。
        调用示例：
        print(await activity.is_scrollable())
        """
        return await self.send_action_until_return('isScrollable')

    async def set_scrollable(self, is_scrollable: bool):
        """
        设置可滚动状态。
        @isScrollable: True表示UI内容超出屏幕可以滚动。
        调用示例：
        await activity.set_scrollable(False)
        """
        dispatch(self.send_action('setScrollable', to_any(is_scrollable)))

    @callback(str)
    async def on_get_clipboard_text(self, text, session_id=0, future_id=0):
        """
        当获取剪贴板文本成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, text)

    async def get_clipboard_text(self) -> str:
        """
        获取剪贴板文本。
        调用示例：
        print(await activity.get_clipboard_text())
        """
        return await self.send_action_until_return('getClipboardText')

    async def set_clipboard_text(self, text: str):
        """
        设置剪贴板文本。
        @text: 要放入设备端剪贴板中的文字。
        调用示例：
        await activity.set_clipboard_text('sscn.byethost3.com')
        """
        dispatch(self.send_action('setClipboardText', to_any(text)))

    async def accessibility_notice(self, text: str):
        """
        发送无障碍通知。
        @text: 要通知的内容。
        调用示例：
        await widget.accessibility_notice('欢迎来到逸时光!')
        """
        dispatch(self.send_action('accessibilityNotice', to_any(text)))

    async def set_touch_exploration(self, enable: bool):
        """
        设置触摸浏览。
        @enable: 启用为True，关闭为False。
        调用示例：
        await widget.set_touch_exploration(False)
        """
        dispatch(self.send_action('setTouchExploration', to_any(enable)))

    @callback(int, int)
    async def on_get_screen_size(self, width, height, **kwargs):
        """
        当获取屏幕大小成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        session_id = kwargs['sessionId'] if 'sessionId' in kwargs else None
        if not session_id:
            return
        future_id = kwargs['futureId'] if 'futureId' in kwargs else 0
        self.set_action_result(session_id, future_id, (width, height,))

    async def get_screen_size(self) -> typing.Tuple[int, int]:
        """
        获取屏幕的大小（返回width和height）
        调用示例：
        print(await activity.get_screen_size())
        """
        return await self.send_action_until_return('getScreenSize')

    async def vibrate(self, millis: int, amplitude: int = -1):
        """
        调用设备的震动功能。
        调用示例：
        await activity.vibrate(100, 40)
        :param millis: 持续时间（毫秒数）。
        :param amplitude: 震动幅度（取值1到255）。
        """
        dispatch(self.send_action('vibrate', to_any(millis, Int64Value), to_any(amplitude, Int32Value)))
